/* Copyright (c) 2004-2022 peigen.info. All rights reserved. */

package info.peigen.hotpot.common.core.result;

import cn.hutool.core.bean.copier.BeanCopier;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.StrUtil;
import info.peigen.hotpot.common.core.annotation.Comment;
import info.peigen.hotpot.common.core.enums.Messageable;
import info.peigen.hotpot.common.core.exception.SystemException;
import info.peigen.hotpot.common.core.result.facade.LinkedHashMapParameterize;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;

import java.io.Serial;
import java.util.function.Supplier;

/**
 * <b>(ResultBase)</b>
 *
 * @author Peigen
 * @version 1.0.0
 * @since 2021/4/9
 */
@EqualsAndHashCode(callSuper = true)
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class ResultBase extends LinkedHashMapParameterize<String, Object> implements Resultable, Messageable {

    /** serialVersionUID */
    @Serial
    @ApiModelProperty(value = "serialVersionUID", position = 92)
    @Comment(value = "serialVersionUID", ignore = true)
    private static final long serialVersionUID = -8702480923545642017L;

    @Builder.Default
    @ApiModelProperty(value = "接口响应状态", position = 93)
    @Comment("接口响应状态")
    private ResultStatus status = ResultStatus.initialization;

    /** 参考 {@link ResultCode} */
    @Builder.Default
    @ApiModelProperty(value = "接口响应代码", position = 94)
    @Comment("接口响应代码")
    private String code = ResultCode.INITIALIZATION.getCode();

    @Builder.Default
    @ApiModelProperty(value = "接口响应描述", position = 95)
    @Comment("接口响应描述")
    private String detail = ResultCode.INITIALIZATION.getMessage();

    @ApiModelProperty(value = "操作员", position = 96)
    @Comment(value = "操作员", ignore = true)
    private String operatorId;

    @Getter
    @Builder.Default
    @ApiModelProperty(value = "接口调用是否收费", position = 97)
    @Comment(value = "接口调用是否收费", ignore = true)
    private boolean fee = false;

    @ApiModelProperty(value = "接口调用费用", position = 98)
    @Comment(value = "费用", ignore = true)
    private String cost;

    @ApiModelProperty(value = "token", position = 99)
    @Comment(value = "token", ignore = true)
    private String token;

    public void markProcessing() {
        this.status = ResultStatus.processing;
        this.code = ResultCode.PROCESSING.code();
        this.detail = ResultCode.PROCESSING.message();
    }

    public boolean success() {
        return status == ResultStatus.success && StrUtil.equalsIgnoreCase(status.code(), ResultCode.SUCCESS.code());
    }

    public boolean processing() {
        return status == ResultStatus.processing || StrUtil.equalsIgnoreCase(status.code(), ResultCode.PROCESSING.code());
    }

    public boolean failure() {
        return status == ResultStatus.failure || StrUtil.equalsIgnoreCase(status.code(), ResultCode.FAILURE.code());
    }

    public void setSuccess() {
        setStatus(ResultStatus.success);
        setCode(ResultCode.SUCCESS.code());
        setDetail(ResultCode.SUCCESS.message());
        setFee(true);
    }

    public void setSuccessNotFee() {
        setStatus(ResultStatus.success);
        setCode(ResultCode.SUCCESS.code());
        setDetail(ResultCode.SUCCESS.message());
        setFee(false);
    }

    public void setProcessing() {
        markProcessing();
    }

    public void setProcessing(String detail) {
        markProcessing();
        setDetail(detail);
    }

    public void setFail() {
        setStatus(ResultStatus.failure);
        setCode(ResultCode.FAILURE.code());
        setDetail(ResultCode.FAILURE.message());
    }

    public void setFail(ResultCode resultCode) {
        setStatus(ResultStatus.failure);
        setCode(resultCode.code());
        setDetail(resultCode.message());
    }

    public void setFail(String detail) {
        setStatus(ResultStatus.failure);
        setCode(ResultCode.FAILURE.code());
        setDetail(detail);
    }

    public void setFail(ResultCode code, String detail) {
        setStatus(ResultStatus.failure);
        setCode(code.code());
        setDetail(detail);
    }

    public void setFail(String code, String detail) {
        setStatus(ResultStatus.failure);
        setCode(code);
        setDetail(detail);
    }

    public static ResultBase succ() {
        return ResultBase.builder()
                .status(ResultStatus.failure)
                .code(ResultStatus.failure.code())
                .detail(ResultStatus.failure.message())
                .build();
    }

    public static ResultBase fail() {
        return ResultBase.builder()
                .status(ResultStatus.failure)
                .code(ResultStatus.failure.code())
                .detail(ResultStatus.failure.message())
                .build();
    }

    public static ResultBase failCode(String code) {
        return ResultBase.builder()
                .status(ResultStatus.failure)
                .code(code)
                .detail(ResultStatus.failure.message())
                .build();
    }

    public static ResultBase failDetail(String detail) {
        return ResultBase.builder()
                .status(ResultStatus.failure)
                .code(ResultStatus.failure.code())
                .detail(detail)
                .build();
    }

    public static ResultBase fail(String code, String detail) {
        return ResultBase.builder()
                .status(ResultStatus.failure)
                .code(code)
                .detail(detail)
                .build();
    }

    public void setGatewayFail(String detail) {
        setStatus(ResultStatus.failure);
        setCode(ResultCode.CHANNEL_GATEWAY_ERROR.code());
        setDetail(detail);
    }

    public void setFailForParaError() {
        setStatus(ResultStatus.failure);
        setCode(ResultCode.PARAMETER_ERROR.code());
        setDetail(ResultCode.PARAMETER_ERROR.message());
    }

    public void setFailForParaError(String detail) {
        setStatus(ResultStatus.failure);
        setCode(ResultCode.PARAMETER_ERROR.code());
        setDetail(detail);
    }

    public void setFailForRequestNoNotUniqueError() {
        setStatus(ResultStatus.failure);
        setCode(ResultCode.REQUEST_NO_NOT_UNIQUE.code());
        setDetail(ResultCode.REQUEST_NO_NOT_UNIQUE.message());
    }

    public void setInternalError(String detail) {
        setStatus(ResultStatus.failure);
        setCode(ResultCode.INTERNAL_ERROR.code());
        setDetail(detail);
    }

    public void setTimeout() {
        setStatus(ResultStatus.failure);
        setCode(ResultCode.CHANNEL_TIMEOUT.code());
        setDetail(ResultCode.CHANNEL_TIMEOUT.message());
    }

    public void setResult(ResultBase result) {
        setStatus(result.getStatus());
        setCode(result.getCode());
        setDetail(result.getDetail());
    }

    @Override
    public String code() {
        return code;
    }

    @Override
    public String message() {
        return detail;
    }

    /**
     * 当status != ResultStatus.success抛出业务异常
     */
    public ResultBase throwIfNotSuccess() {
        if (!success()) {
            throw new SystemException(this.getDetail(), this.getCode());
        }
        return this;
    }

    /**
     * 当status != ResultStatus.success抛出业务异常
     *
     * @param errorSupplier 继承BusinessException的异常
     */
    public <X extends SystemException> void throwIfNotSuccess(Supplier<X> errorSupplier) throws X {
        if (!success()) {
            throw errorSupplier.get();
        }
    }

    /**
     * 当status == ResultStatus.failure抛出业务异常
     */
    public ResultBase throwIfFailure() {
        if (status == ResultStatus.failure) {
            throw new SystemException(this.getDetail(), this.getCode());
        }
        return this;
    }

    /**
     * 把当前结果对象属性复制到target对象
     *
     * @param target           目标对象
     * @param ignoreProperties 忽略参数名
     */
    public ResultBase copyTo(Object target, String... ignoreProperties) {
        CopyOptions copyOptions = CopyOptions.create()
                .setIgnoreNullValue(true)
                .setIgnoreError(true)
                .setIgnoreProperties(ignoreProperties);
        BeanCopier copier = BeanCopier.create(this, target, copyOptions);
        copier.copy();
        return this;
    }

}